/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi_utils.h"
#include "../ohoslog/ohos_log.h"
#include <codecvt>
#include <cstdint>
#include <cstdio>
#include <locale>
#include <string.h>
#include <string>


const int32_t MAX_STR_LENGTH = 2048;

napi_value NapiUtil::SetNapiCallInt32(const napi_env &env, const int32_t intValue) {
    napi_value result;
    napi_create_int32(env, intValue, &result);
    return result;
}

napi_value NapiUtil::SetNapiCallBool(napi_env env, bool value) {
    napi_value temp;
    napi_value result;
    napi_create_int32(env, value == true ? 1 : 0, &temp);
    napi_coerce_to_bool(env, temp, &result);
    return result;
}

int NapiUtil::StringToInt(std::string value) { return atoi((char *)value.c_str()); }

int NapiUtil::StringToLong(std::string value) { return atol((char *)value.c_str()); }

float NapiUtil::StringToFloat(std::string value) { return std::stof(value); }

bool NapiUtil::StringToBool(const std::string value) { return value == "true" ? true : false; }

std::string NapiUtil::GetAvroStringTypeValue(const std::string &input) {
    size_t typePos = input.find("\"type\"");
    if (typePos == std::string::npos) {
        return "";
    }

    size_t colonPos = input.find(':', typePos);
    if (colonPos == std::string::npos) {
        return "";
    }

    size_t startQuote = input.find('"', colonPos + 1);
    if (startQuote == std::string::npos) {
        return "";
    }

    size_t endQuote = input.find('"', startQuote + 1);
    if (endQuote == std::string::npos) {
        return "";
    }

    return input.substr(startQuote + 1, endQuote - startQuote - 1);
}

bool NapiUtil::IsFixedTypeByavroStr(const std::string &input) {
    std::string typeName = GetAvroStringTypeValue(input);
    if (typeName == "fixed") {
        return true;
    }
    return false;
}

bool NapiUtil::IsRecordTypeByavroStr(const std::string &input) {
    std::string typeName = GetAvroStringTypeValue(input);
    if (typeName == "record") {
        return true;
    }
    return false;
}

bool NapiUtil::IsArray(napi_env env, napi_value value) {
    napi_valuetype type;
    napi_status status = napi_typeof(env, value, &type);
    if ((status != napi_ok) || (type != napi_object)) {
        LOGERROR("Is not object!");
        return false;
    }
    bool isArray;
    status = napi_is_array(env, value, &isArray);
    if (status != napi_ok) {
        LOGERROR("Check buffer failed！");
        return false;
    }

    return isArray;
}

bool NapiUtil::IsArrayBuffer(napi_env env, napi_value value) {
    napi_valuetype type;
    napi_status status = napi_typeof(env, value, &type);
    if ((status != napi_ok) || (type != napi_object)) {
        LOGERROR("Is not object!");
        return false;
    }
    bool is_buffer;
    status = napi_is_arraybuffer(env, value, &is_buffer);
    if (status != napi_ok) {
        LOGERROR("Check buffer failed！");
        return false;
    }

    return is_buffer;
}

std::string NapiUtil::GetConstructorName(napi_env env, napi_value obj) {
    napi_value constructor;
    if (napi_get_named_property(env, obj, "constructor", &constructor) != napi_ok) {
        LOGERROR("Failed to napi_get_named_property");
        return "";
    }

    napi_value name_prop;
    if (napi_get_named_property(env, constructor, "name", &name_prop) != napi_ok) {
        LOGERROR("Failed to napi_get_named_property");
        return "";
    }

    size_t len = 0;
    napi_status status = napi_get_value_string_utf8(env, name_prop, nullptr, 0, &len);
    if (status != napi_ok) {
        LOGERROR("Failed to get string length！");
        return "";
    }

    std::unique_ptr<char[]> name(new char[len + 1]);
    if (!name) {
        LOGERROR("Failed to allocate memory for constructor name");
        return "";
    }
    status = napi_get_value_string_utf8(env, name_prop, name.get(), len + 1, nullptr);
    if (status != napi_ok) {
        LOGERROR("Failed to get string length: %d", status);
        return "";
    }
    name[len] = 0;

    return std::string(name.get(), len);
}

bool NapiUtil::IsMap(napi_env env, napi_value value) {
    napi_valuetype type;
    napi_status status = napi_typeof(env, value, &type);
    if (status != napi_ok) {
        LOGERROR("Is not map(val is %{public}d)!", type);
        return false;
    }

    if (type != napi_object) {
        LOGERROR("Is not map!");
        return false;
    }

    bool is_array;
    status = napi_is_array(env, value, &is_array);
    if ((status != napi_ok) || is_array) {
        LOGERROR("map is not array");
        return false;
    }

    std::string name = GetConstructorName(env, value);
    if (name.empty() || (name.compare("Map") != 0)) {
        return false;
    }

    return true;
}

void NapiUtil::JsValueToString(const napi_env &env, const napi_value &value, const int32_t bufLen,
                               std::string &target) {
    if (bufLen <= 0 || bufLen > MAX_STR_LENGTH) {
        LOGERROR("string too long malloc failed");
        return;
    }

    std::unique_ptr<char[]> buf = std::make_unique<char[]>(bufLen);
    if (buf.get() == nullptr) {
        LOGERROR("nullptr js object to string malloc failed");
        return;
    }
    (void)memset(buf.get(), 0, bufLen);
    size_t result = 0;
    napi_get_value_string_utf8(env, value, buf.get(), bufLen, &result);
    target = buf.get();
}